package com.hama.iotrust.common.mybatis.criteria;

import com.hama.iotrust.common.mybatis.criteria.Criteria;
import com.hama.iotrust.common.mybatis.criteria.CriteriaException;
import com.hama.iotrust.common.mybatis.criteria.Sort;

/**
 * 排序条件
 *
 * @author 金建强(ptma@163.com)
 * @version 1.0.0
 * @since 2013-4-2 11:43:01
 */
public class Sort implements Cloneable {

    /**
     * 排序顺序枚举， ASC|DESC
     */
    private enum SortDir {
        ASC("ASC"), DESC("DESC");

        private String sql;

        private SortDir(String asSql){
            this.sql = asSql;
        }

        public String toSql() {
            return sql;
        }
    }

    /**
     * 可能存在空值时的排序顺序枚举
     */
    private enum NullDir {

        /** NULL 值在前 */
        NULLS_FIRST(SortDir.ASC),
        /** NULL 值在后 */
        NULLS_LAST(SortDir.DESC);

        private SortDir sort;

        private NullDir(SortDir dir){
            this.sort = dir;
        }

        public String toSql(String nullableColumn) {
            return "(CASE WHEN " + nullableColumn + " IS NULL THEN 0 ELSE 1 END) " + sort.toSql();
        }
    }

    private String  m_propName;
    private String  m_columnName = null;

    private SortDir sort;
    private NullDir nullDir;

    private Sort(String propertyName, SortDir sort){
        this.m_propName = propertyName;
        this.sort = sort;
    }

    public static Sort asc(String propertyName) {
        return new Sort(propertyName, SortDir.ASC);
    }

    public static Sort desc(String propertyName) {
        return new Sort(propertyName, SortDir.DESC);
    }

    public Sort nullsFirst() {
        nullDir = NullDir.NULLS_FIRST;
        return this;
    }

    public Sort nullsLast() {
        nullDir = NullDir.NULLS_LAST;
        return this;
    }

    public String getSql() {
        StringBuilder builder = new StringBuilder();

        if (m_columnName == null) { // property not bound
            builder.append("< not bound:").append(m_propName).append(">");
        } else {
            if (nullDir != null) {
                builder.append(nullDir.toSql(m_columnName) + ", ");
            }
            builder.append(m_columnName);
            builder.append(' ' + sort.toSql() + ' ');
        }

        return builder.toString();
    }

    @Override
    protected Sort clone() {
        try {
            return (Sort) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError("对象不支持 clone()");
        }
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((m_columnName == null) ? 0 : m_columnName.hashCode());
        result = prime * result + ((m_propName == null) ? 0 : m_propName.hashCode());
        result = prime * result + ((nullDir == null) ? 0 : nullDir.hashCode());
        result = prime * result + ((sort == null) ? 0 : sort.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (getClass() != obj.getClass()) return false;
        Sort other = (Sort) obj;
        if (m_columnName == null) {
            if (other.m_columnName != null) return false;
        } else if (!m_columnName.equals(other.m_columnName)) return false;
        if (m_propName == null) {
            if (other.m_propName != null) return false;
        } else if (!m_propName.equals(other.m_propName)) return false;
        if (nullDir != other.nullDir) return false;
        if (sort != other.sort) return false;
        return true;
    }

    public void bind(Criteria<?> crit) throws CriteriaException {
        m_columnName = crit.toColumnName(m_propName);
    }
}
